/**
 * 消息发布器
 *
 * Created by snowingsea on 2020/01/21.
 */
// const redis = require('./index');
const utility = require('../utility');
const fileElements = require('../file-elements');

/**
  * 私人频道，只用于服务端
  * @param {String} userId 用户id
  * @return {String}
  */
function privateChannel(userId) {
  return userId.toString();
}

/**
  * 系统管理员频道
  * @return {String}
  */
function administratorChannel() {
  return 'administrator';
}

/**
  * 系统管理员用户管理频道
  * @return {String}
  */
function administratorUsersChannel() {
  return 'administrator/users';
}

/**
  * 系统管理员许可证频道
  * @return {String}
  */
function administratorLicenseChannel() {
  return 'administrator/license';
}

/**
  * 测试经理频道
  * @param {String} userId 用户id
  * @return {String}
  */
function managerChannel(userId) {
  return `manager/${userId}`;
}

/**
  * 测试人员频道
  * @param {String} userId 用户id
  * @return {String}
  */
function testerChannel(userId) {
  return `tester/${userId}`;
}
/**
  * 测试人员许可证频道
  * @return {String}
  */
function testerLicenseChannel() {
  return 'tester/license';
}

/**
  * 项目频道
  * @param {String} userId 用户id
  * @return {String}
  */
function projectsChannel(userId) {
  return `tester/${userId}/projects`;
}

/**
  * 单个项目频道
  * @param {String} projectId 项目id
  * @return {String}
  */
function projectChannel(projectId) {
  return `projects/${projectId}`;
}

/**
  * 项目版本频道
  * @param {String} projectId 项目id
  * @return {String}
  */
function projectVersionsChannel(projectId) {
  return `projects/${projectId}/project-versions`;
}

/**
  * 单个项目版本频道
  * @param {String} projectVersionId 项目版本id
  * @return {String}
  */
function projectVersionChannel(projectVersionId) {
  return `project-versions/${projectVersionId}`;
}

/**
  * 文件频道
  * @param {String} projectVersionId 项目版本id
  * @return {String}
  */
function filesChannel(projectVersionId) {
  return `project-versions/${projectVersionId}/files`;
}

/**
  * 单个文件频道
  * @param {String} fileId 文件id
  * @return {String}
  */
function fileChannel(fileId) {
  return `files/${fileId}`;
}

/**
  * 项目版本函数频道
  * @param {String} projectVersionId 项目版本id
  * @return {String}
  */
function projectVersionFunctionsChannel(projectVersionId) {
  return `project-versions/${projectVersionId}/functions`;
}

/**
  * 文件函数频道
  * @param {String} fileId 文件id
  * @return {String}
  */
function fileFunctionsChannel(fileId) {
  return `files/${fileId}/functions`;
}

/**
  * 单个函数频道
  * @param {String} functionId 函数id
  * @return {String}
  */
function functionChannel(functionId) {
  return `functions/${functionId}`;
}

/**
  * 测试用例频道
  * @param {String} functionId 函数id
  * @return {String}
  */
function testsChannel(functionId) {
  return `functions/${functionId}/tests`;
}

/**
  * 单个测试用例频道
  * @param {String} testId 测试用例id
  * @return {String}
  */
function testChannel(testId) {
  return `tests/${testId}`;
}

/**
  * 用户任务频道
  * @param {String} userId 用户id
  * @return {String}
  */
function testerTasksChannel(userId) {
  return `tester/${userId}/tasks`;
}
/**
 * 所有任务频道
 */
function allTasksChannel() {
  return 'all/tasks';
}

/**
  * 项目版本任务频道
  * @param {String} projectVersionId 项目版本id
  * @return {String}
  */
function projectVersionTasksChannel(projectVersionId) {
  return `project-versions/${projectVersionId}/tasks`;
}

/**
  * 单个任务频道
  * @param {String} taskId 任务id
  * @return {String}
  */
function taskChannel(taskId) {
  return `tasks/${taskId}`;
}

/**
  * 缺陷消息频道
  * @param {String} projectVersionId 项目版本id
  * @return {String}
  */
function fileDefectChannel(projectVersionId) {
  return `project-versions/${projectVersionId}/fileDefects`;
}

/**
 * 代码缺陷频道
 * @param {String} functionId 函数id
 * @return {String}
 */
function codeDefectChannel(functionId) {
  return `functions/${functionId}/codeDefects`;
}

function codeInstrumentationChannel() {
  return 'code-instrumentations';
}

/**
  * 发布消息
  * @param {String} channel 频道
  * @param {String} cmd 操作
  * @param {String} domain 所在域
  * @param {Object} data 数据
  */
function publish(channel, cmd, domain = null, data = null) {
  const msg = { cmd, channel };
  if (domain) {
    msg.domain = domain;
  }
  if (data) {
    msg.data = data;
  }
  // const client = redis.getClient();
  const { MessagePublisher } = require('../amqp/pub-sub/publisher'); // 避免循环引用
  const client = MessagePublisher.websocketMessageBus;
  if (client) {
    client.publish(channel.toString(), JSON.stringify(msg));
  }
}

/**
  * 发布登出消息
  * @param {String} userId 用户id
  * @param {String} currentAccessToken 当前用户token
  */
function publishLogoutMessage(userId, currentAccessToken = null) {
  publish(privateChannel(userId), 'logout', 'user', { currentAccessToken });
}

/**
  * 发布许可证消息
  * @param {String} cmd 操作
  * @param {Object} data 数据
  */
function publishLicenseMessage(cmd, data = null) {
  publish(administratorChannel(), cmd, 'license', data);
  publish(administratorLicenseChannel(), cmd, 'license', data);
  if (data && data.status) {
    data = { licenseStatus: data.status };
  }
  publish(testerLicenseChannel(), cmd, 'license', data);
}

/**
  * 发布用户消息
  * @param {String} cmd 操作
  * @param {Object} data 数据
  */
function publishUserMessage(cmd, data) {
  publish(administratorChannel(), cmd, 'user', data);
  publish(administratorUsersChannel(), cmd, 'user', data);
}

/**
  * 发布项目消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、projectId字段
  */
function publishProjectMessage(cmd, data) {
  const { creatorId, projectId } = data;
  publish(testerChannel(creatorId), cmd, 'project', data);
  publish(projectsChannel(creatorId), cmd, 'project', data);
  publish(projectChannel(projectId), cmd, 'project', data);
}

/**
  * 发布项目版本消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、projectId、versionId字段
  */
function publishProjectVersionMessage(cmd, data) {
  const { creatorId, projectId, versionId } = data;
  publish(testerChannel(creatorId), cmd, 'projectVersion', data);
  publish(projectsChannel(creatorId), cmd, 'projectVersion', data);
  publish(projectChannel(projectId), cmd, 'projectVersion', data);
  publish(projectVersionsChannel(projectId), cmd, 'projectVersion', data);
  publish(projectVersionChannel(versionId), cmd, 'projectVersion', data);
}

/**
  * 发布文件消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、projectId、versionId、fileId字段
  */
function publishFileMessage(cmd, data) {
  const { creatorId, versionId, fileId } = data;
  data.id = fileId;
  publish(testerChannel(creatorId), cmd, 'file', data);
  publish(filesChannel(versionId), cmd, 'file', data);
  publish(fileChannel(fileId), cmd, 'file', data);
}

/**
  * 发布根目录消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、projectId、versionId、fileId字段
  */
function publishRootDirectoryMessage(cmd, data) {
  const { creatorId, projectId, versionId } = data;
  publishFileMessage(cmd, data);
  publish(projectsChannel(creatorId), cmd, 'file', data);
  publish(projectChannel(projectId), cmd, 'file', data);
  publish(projectVersionsChannel(projectId), cmd, 'file', data);
  publish(projectVersionChannel(versionId), cmd, 'file', data);
}

/**
  * 发布函数消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、versionId、fileId、functionId字段
  */
function publishFunctionMessage(cmd, data) {
  const {
    creatorId,
    versionId,
    fileId,
    functionId,
  } = data;
  data.id = functionId;
  fileElements.formatCoverageInfo(data, true);
  publish(testerChannel(creatorId), cmd, 'function', data);
  publish(projectVersionFunctionsChannel(versionId), cmd, 'function', data);
  publish(fileFunctionsChannel(fileId), cmd, 'function', data);
  publish(functionChannel(functionId), cmd, 'function', data);
}

/**
  * 发布测试用例消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、functionId、testId字段
  */
function publishTestCaseMessage(cmd, data) {
  const { creatorId, functionId, testId } = data;
  if (data.resultKind) {
    if (!data.result) {
      data.result = {};
    }
    data.result.kind = data.resultKind;
  }
  publish(testerChannel(creatorId), cmd, 'testCase', data);
  publish(testsChannel(functionId), cmd, 'testCase', data);
  publish(testChannel(testId), cmd, 'testCase', data);
}

/**
  * 发布测试用例消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、versionId、taskId字段
  */
function publishTaskMessage(cmd, data) {
  const { creatorId, versionId, taskId } = data;
  publish(testerChannel(creatorId), cmd, 'task', data);
  publish(testerTasksChannel(creatorId), cmd, 'task', data);
  publish(projectVersionTasksChannel(versionId), cmd, 'task', data);
  publish(taskChannel(taskId), cmd, 'task', data);
  publish(allTasksChannel(), cmd, 'task', data);
}

function publishSingleTaskMessage(cmd, data) {
  const { taskId } = data;
  publish(taskChannel(taskId), cmd, 'task', data);
}

function publishValidationMessage(cmd, data) {
  const { creatorId } = data;
  publish(privateChannel(creatorId), cmd, 'hash', data);
}

/**
  * 发布缺陷消息
  * @param {String} cmd 操作
  * @param {Object} data 数据，需要带creatorId、versionId、ruleId
  */
function publishFileDefectMessage(cmd, data) {
  const { versionId } = data;
  publish(projectVersionChannel(versionId), cmd, 'fileDefect', data);
  publish(fileDefectChannel(versionId), cmd, 'fileDefect', data);
}

/**
 * 发布代码缺陷消息
 * @param {String} cmd 操作
 * @param {Object} data 数据，需要带creatorId、versionId、ruleId
 */
function publishCodeDefectMessage(cmd, data) {
  const { versionId, functionId } = data;
  if (data._id) {
    data.defectId = data._id;
    delete data._id;
  }
  publish(projectVersionChannel(versionId), cmd, 'codeDefect', data);
  publish(codeDefectChannel(functionId), cmd, 'codeDefect', data);
}

function publishProgressMessage(cmd, data) {
  const chan = utility.getSessionId();
  if (!chan) {
    return 0;
  }
  if (data.progress) {
    data.progress = Math.round(data.progress * 10000) / 10000;
  }
  return publish(chan, cmd, 'progress', data);
}

function publishCodeInstrumentationMessage(cmd, data) {
  const { creatorId, versionId } = data;
  publish(testerChannel(creatorId), cmd, 'testCase', data);
  publish(projectVersionChannel(versionId), cmd, 'codeInstrumentation', data);
  publish(codeInstrumentationChannel(), cmd, 'codeInstrumentation', data);
}

/**
  * Export model definition object.
  */
module.exports = {
  publish,
  publishCodeDefectMessage,
  publishCodeInstrumentationMessage,
  publishLogoutMessage,
  publishLicenseMessage,
  publishUserMessage,
  publishProjectMessage,
  publishProjectVersionMessage,
  publishFileMessage,
  publishFileDefectMessage,
  publishRootDirectoryMessage,
  publishFunctionMessage,
  publishTestCaseMessage,
  publishTaskMessage,
  publishSingleTaskMessage,
  publishValidationMessage,
  publishProgressMessage,

  privateChannel,
  managerChannel,
};
exports = Object.assign(exports, module.exports);
